<?php

namespace Gnway\Ticket\Rest\Annotation\Parser;

use Gnway\Ticket\Rest\Annotation\Controller;
use Gnway\Ticket\Rest\Annotation\RequestMapping;
use ReflectionClass;
use ReflectionException;
use ReflectionMethod;

/**
 * Class RequestMappingParser
 */
class RequestMappingParser extends AbstractParser
{

  /**
   * Controller Annotations
   *
   * @var array
   */
  protected array $controllerAnnotations;

  /**
   * RequestMappingParser constructor.
   *
   * @param array $controllerAnnotations
   * @throws ReflectionException
   */
  public function __construct(array $controllerAnnotations)
  {
    parent::__construct();

    $this->controllerAnnotations = $controllerAnnotations;
  }

  /**
   * Parse RequestMapping
   *
   * @return array
   */
  public function parse(): array
  {
    $mapping = [];

    foreach ($this->controllerAnnotations as $controller) {
      if (!(($controller->annotation instanceof Controller)
        && ($controller->reflection instanceof ReflectionClass))) {
        continue;
      }

      foreach ($controller->reflection->getMethods(ReflectionMethod::IS_PUBLIC) as $method) {
        $route = $this->reader->getMethodAnnotation($method, RequestMapping::class);
        if (!($route instanceof RequestMapping)) {
          continue;
        }
        $routes = $this->makeRoutes(
          $controller->annotation->getPrefix(), $controller->annotation->getSuffix(), $route->getValue() ?: '',
          $controller->reflection->getName(), $method->getName(), $route->getMethod(), $route->getValidation()
        );
        /*if ($intersect = array_intersect_key($routes, $mapping)) {
          throw new RuntimeException('Routing "' . key($intersect) . '" cannot be defined repeatedly.');
        }*/
        $mapping = array_merge($mapping, $routes);
      }
    }

    return $mapping;
  }

  /**
   * Append to routes.
   *
   * @param string $prefix
   * @param string $suffix
   * @param mixed  $value
   * @param string $class
   * @param string $action
   * @param mixed  $method
   * @param array  $validation
   * @return array
   */
  protected function makeRoutes(string $prefix, string $suffix, $value,
                                string $class, string $action, array $method, array $validation): array
  {
    if (!($class && $action)) {
      return [];
    }

    $result = [];
    if (is_array($value)) {
      foreach ($value as $v) {
        $result = array_merge($result, $this->makeRoutes($prefix, $suffix, $v, $class, $action, $method, $validation));
      }
    } else {

      //To deal with the prefix, default is class name if prefix was empty.
      /*if ('' === $prefix) {
        $className = explode('\\', $class);
        $prefix = lcfirst(end($className));
      }*/

      /*$prefix = trim($prefix, " \t\n\r\0\x0B\\");
      $mapping = trim((string)$value, " \t\n\r\0\x0B\\");

      //To deal with the route, default is method name if route was empty.
      if ('' === $mapping) {
        $mapping = '/' . lcfirst($action);
      } else {
        $mapping = '/' . trim($mapping, '/');
      }

      //Merge request mapping.
      if ($prefix !== '') {
        if ($mapping !== '/') {
          $mapping = $prefix !== '/' ? $prefix . $mapping : $mapping;
        } else {
          $mapping = $prefix;
        }
      }
      if ($mapping !== '/' && ($suffix = trim($suffix, '.')) !== '') {
        $mapping .= '.' . $suffix;
      }*/

      $value = (string)$value;

      //Validation rules
      if ($validation && preg_match_all('/\{([a-z][a-z0-9]*?)\}/i', $value, $matches) && $matches[1]) {
        $validation = array_combine($matches[1], array_values($validation));
      } else {
        $validation = [];
      }

      $result[] = [
        'class'      => $class,
        'action'     => $action,
        'method'     => $method,
        'mapping'    => $value,
        'prefix'     => $prefix,
        'suffix'     => $suffix,
        'validation' => $validation,
      ];
    }

    return $result;
  }
}